#Copyright 2012 Daniel C.(llaniscudani@gmail.com)

#This file is part of Radius3000.

#Radius3000 is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.

#Radius3000 is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with Radius3000.  If not, see <http://www.gnu.org/licenses/>.

#eap.py is the EAP Library

__version__ = "0.1"

import logging
import random



class EAPRequestHandler:
    #EAP packet codes
    #1: Request
    #2: Response
    #3: Sucess
    #4: Failure
    #EAP packet types
    #1: Identity
    #2: Notification
    #3: NAK
    #25: PEAP
    #29: EAP-MS-CHAP-V2
                        
                        
    def __init__(self):
        #define identifier
        self.identifier = 0


    def eap_packet(self, packet_code=1, packet_type=None, packet_data=None):
        #EAP packet
        #codes:
        #1:Request
        #2:Response
        #3:Sucess
        #4:Failure
        p = packet_code.to_bytes(1, 'big')
        # id + 1
        p += (self.identifier + 1).to_bytes(1, 'big')
        # len(2bytes)
        if packet_code == 1 or packet_code == 2:
            #request or response
            if packet_data != None:
                p += (5 + len(packet_data)).to_bytes(2, 'big')            
                # type of eap packet
                p += packet_type.to_bytes(1, 'big')
                #data of packet
                p += packet_data
            else:
                p += b'\x00\x05'
                # type of eap packet
                p += packet_type.to_bytes(1, 'big')
        else:
            #if a sucess 3 or failure 4
            #packet len is 4
            p += b'\x00\x04'

        return p


    def peap_start(self):      
        logging.debug("[PEAP] Send PEAP Start")
        # packet for start peap session
        # code 1 request
        # type of eap packet 25 = peap
        # peap flags 00100000 = peap start
        p = int('00100000',2).to_bytes(1,'big')
        return self.eap_packet(1, 25, p)


    def eap_tlv_crypto_binding_request(self):
        logging.debug("[PEAP] tlv crypto binding")
        # packet for start tlv crypto binding
        # code 1 request
        # type of eap packet 33 = eap-tlv
        #tlv data
        #byte1 reserved(0x00)
        p = b'\x00'
        #attribute id (0x04 for crypto binding)
        p += b'\x04'
        #length(2bytes)
        #-reserved(4bits)
        #-length(12bits)MAX=4095
        p += (40).to_bytes(2, 'big')
        #reserved 1(3bytes) = 0x00 0x00 0x00
        p += b'\x00\x00\x00'
        #hash protocol bitmask. 0x01=SHA1. 0x02=SHA256
        p += b'\x01'
        #nonce. 256bit aleatory
        p += (random.getrandbits(256)).to_bytes(32, 'big')
        return self.eap_packet(1, 33, p)


    def eap_tlv_result_avp(self):
        logging.debug("[PEAP] tlv crypto binding")
        # packet
        # code 1 request
        # type of eap packet 33 = eap-tlv
        #tlv data
        #mandatory 1bit
        #reserved 1bit
        #tlv type 14bits
        p = b'\x80\x03'
        #len=2(2bytes)
        p += b'\x00\x02'
        #status(2bytes)
        #1 sucess
        #2 failure
        p += b'\x00\x01'
        return self.eap_packet(1, 33, p)
      
      
    def eap_vendor_specific_tlv(self, vendor_id):
        #mandatory 1bit
        #reserved...
        #tlv type = 7
        p = b'\x80\x07'
        #len >= 4
        p += b'\x00\x04'
        #vendor first byte reserved, id=3bytes
        p += b'\x00' + vendor_id.to_bytes(3, 'big')
        return p
            
        
    def eap_payload_tlv(self, eap, tlv=b''):
        #packet for encapsulate full eap packet in tlv
        logging.debug("[EAP] eap payload tlv")
        ###data###
        #mandatory = 1(1bit)
        #reserved = 0(1bit)
        #avp type = 9(14bits)
        p = b'\x80\x09' + (len(eap + tlv)).to_bytes(2, 'big') + eap + tlv
        #make eap packet
        # code 1 request
        # type of eap packet 33 = eap-tlv
        return self.eap_packet(1, 33, p)
        

    def peap_parse(self, eap):
        #input eap packet
        #flags contain version of peap
        #and attributes as length included
        self.flags = eap[0:1]
        
        if self.flags == b'\x80':
            #if packet has length...
            length = int.from_bytes(eap[1:5], 'big')
            tls = eap[5:]
            #check len...
            if len(tls) != length: return
                
        elif self.flags == b'\x00':
            tls = eap[1:]
        
        #content of peap is tls?
        #(no pop this bytes, only for check)
        #if tls[0:2] == b'\x00\x17':
        #    if len(tls) == int.from_bytes(tls[2:3], 'big'):
        return tls


    def peap_packet(self, tls):
        #packet for make peap packet
        #code 1 request
        #type of eap packet 25=peap
        #peap flags 00000000 = only peap v0
        p = b'\x00' + tls
        return self.eap_packet(1, 25, p)
               

    def request_identity(self):
        #request code = 1
        #identity type = 1
        return self.eap_packet(1, 1)


    def sucess(self):
        #sucess packet = code 3
        return self.eap_packet(3)


    def failure(self):
        #failure packet = code 4
        return self.eap_packet(4)


    def parse(self, packet):
        # code1+id1+len2+data...
        logging.debug('[EAP] Parsing EAP Packet')
        
        # dict packet codes
        self.p_code = { 1:'Request',
                        2:'Response',
                        3:'Sucess',
                        4:'Failure' }
                
        # dict packet types (some...)
        self.p_type = { 1:'Identity',
                        2:'Notification',
                        3:'NAK',
                        25:'PEAP',
                        29:'EAP-MS-CHAP-V2' }
        
        #packet code
        self.code = int.from_bytes(packet[0:1], 'big')
        # packet identifier
        self.identifier = int.from_bytes(packet[1:2], 'big')
        # packet length
        self.length = int.from_bytes(packet[2:4], 'big')
        # check len
        if self.length != len(packet):
            return False
        self.type = int.from_bytes(packet[4:5], 'big')
        self.data = packet[5:self.length]
        
        logging.debug('[EAP] EAP Parsed')
        
        return self.data
        ######## PARSED ########
        
        
